package com.demo.aop;
import com.demo.annotation.Around;
import com.demo.enhance.AopMethodInterceptor;
import com.demo.enhance.DefaultAopMethodInterceptor;
import com.demo.enhance.ReflectionMethodInvocation;
import com.demo.util.StringUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public class ProxyHelper {

    /**
     * 根据额外功能对象 来寻找带有@Around注解的对象，同时，该对象的 参数一定是JoinPoint类型的。
     * 一个额外功能类只有一个@Around注解。。。
     * @param ext
     * @return
     */
    public Method getAroundMethod(Object ext){
        Method res = null;
        Method[] declaredMethods = ext.getClass().getDeclaredMethods();
        for (Method method : declaredMethods) {
            if(method.isAnnotationPresent(Around.class)){
                res = method;
                break;
            }
        }
        return  res;
    }

    /**
     * 根据 原始对象 及其 需要增强的功能类 之间的关系，构建 执行链
     * @param aspectWrappers
     * @param method
     * @return
     */
    public List<Object> chainBuilder(List<AspectWrapper>aspectWrappers,Method method){
        List<Object>featureObjects=new ArrayList<>();
        aspectWrappers.forEach(asp->{
            PointCutWrapper pointCutWrapper = asp.getPointCutWrapper();
            if(pointCutWrapper.getLevel()==1||pointCutWrapper.getLevel()==2){
                featureObjects.add(asp.getExtObject());
            }else {
                if(pointCutWrapper.getLevel()==3){//方法切入点
                    String cutMethodName = StringUtils.getClassNameByFullName(pointCutWrapper.getDestination());
                    if(cutMethodName.equals(method.getName())){
                        featureObjects.add(asp.getExtObject());
                    }
                }
            }
        });
        return featureObjects;
    }

    public Object helpCreateProxyObject(AopWrapper wrapper){
        Object target = wrapper.getTarget();//真实对象
        InvocationHandler handler = new InvocationHandler() {
            /**
             * 动态代理 是以整个对象为单位一起代理，但是 里面代理的方法可以有差距
             * @param proxy 代理对象 没啥用
             * @param method 接口的抽象方法 abstract 类型的
             * @param args 执行参数
             * @return 执行结果
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object ret = null;
                /**
                 * 这里要根据条件，来控制 在方法上加的额外功能类
                 */
                List<Object>features = chainBuilder(wrapper.getAspectWrappers(),method);
                     //额外功能链 该链里面的对象要符合AopMethodInterceptor接口 把@Around注解的类转换成 符合该接口的实现类类
                List<AopMethodInterceptor>proxyChain=constructChain(features);
                ReflectionMethodInvocation invocation = new ReflectionMethodInvocation(target,proxy,method,args,proxyChain);
                ret =invocation.proceed(); //执行链 执行结果 返回
                return ret;
            }
        };
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),handler );
    }
// 根据额外功能 封装成 链
    public List<AopMethodInterceptor> constructChain(List<Object> exts){
        List<AopMethodInterceptor> list = new ArrayList<>();
        exts.forEach(aop->{
            AopMethodInterceptor interceptor = new DefaultAopMethodInterceptor(aop,getAroundMethod(aop));
            list.add(interceptor);
        });
        return list;
    }


}
